#include "stdafx.h"

#include <MyFC/Control.h>
#include <MyFC/Container.h>
#include <MyFC/Utility.h>
#include <MyFC/WndTipCtrl.h>
#include <typeinfo>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//#define DEBUG_ENTER_LEAVE

namespace My {

Control::Control()
	: m_pParent(0), m_Index()
{
	m_Inside = false;
	m_ButtonPressed = false;
	m_Dragging = false;
	m_PaintOutside = false;
}
	
Control::~Control()
{
	if (m_pParent != 0)
		m_pParent->Remove(this);
}

bool Control::Enabled() const
{
	return true;
}

CPoint Control::GetWindowOffset() const
{
	if (Parent() == 0)
		return CPoint(0, 0);
	else
		return Parent()->GetWindowOffset() + Offset();
}

CRect Control::GetWindowRect() const
{
	if (Parent() == 0)
		return CRect(0, 0, 0, 0);
	else {
		CRect r = Bounds();
		r.OffsetRect(Parent()->GetWindowOffset());
		return r;
	}
}

CWnd * Control::GetWindow() const
{
	return Parent() ? Parent()->GetWindow() : 0;
}
			
WindowTipCtrl * Control::GetHintWindow() const
{
	return Parent() ? Parent()->GetHintWindow() : 0;
}

void Control::UpdateCursor()
{
	if (GetWindow() != 0) {
		CPoint p; GetCursorPos(&p);
		p = ScreenToClient(p);
		if (ClientRect().PtInRect(p))
			GetWindow()->SendMessage(WM_SETCURSOR, WPARAM(GetWindow()->m_hWnd), HTCLIENT);
	}
}			

void Control::UpdateHint()
{
	WindowTipCtrl * pTipCtrl = GetHintWindow();
	if (pTipCtrl != 0 && Hover()) 
	{
		String sHint = Hint();
		pTipCtrl->UpdateWndTipText(sHint.c_str(), GetWindow());
		pTipCtrl->Activate(!sHint.empty());
	}
}

void Control::UpdateBounds()
{
	Invalidate();
}

void Control::UpdateVisible()
{
	if (Parent() != 0)
		Parent()->Invalidate();
}

void Control::UpdateFont()
{
	ProcessEvent(FontEvent());
}

void Control::ProcessEvent(Event & event)
{
	if (event.Is<ActivateEvent>())
	{
		ActivateEvent & e = event.As<ActivateEvent>();
		HandleActivate(e.m_Active);
		return;
	}
	if (event.Is<FocusEvent>())
	{
		HandleFocus();
		return;
	}
	if (event.Is<UnfocusEvent>())
	{
		HandleUnfocus();
		return;
	}
	if (event.Is<KeyDownEvent>())
	{
		KeyEvent & e = event.As<KeyEvent>();
		HandleKeyDown(e.m_Key, e.m_Count, e.m_Flags);
		return;
	}
	if (event.Is<KeyUpEvent>())
	{
		KeyEvent & e = event.As<KeyEvent>();
		HandleKeyUp(e.m_Key, e.m_Count, e.m_Flags);
		return;
	}
	if (event.Is<LeftDoubleClickEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleLeftDoubleClick(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<LeftDownEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleLeftDown(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<LeftUpEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleLeftUp(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<MouseMoveEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleMouseMove(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<MouseEnterEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleMouseEnter(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<MouseLeaveEvent>())
	{
		HandleMouseLeave();
		return;
	}
	if (event.Is<RightDoubleClickEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleRightDoubleClick(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<RightDownEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleRightDown(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<RightUpEvent>())
	{
		MouseEvent & e = event.As<MouseEvent>();
		HandleRightUp(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<CursorEvent>())
	{
		CursorEvent & e = event.As<CursorEvent>();
		e.m_Handled = HandleCursor(e.m_Flags, e.m_Point);
		return;
	}
	if (event.Is<UpdateUIEvent>())
	{
		HandleUpdateUI();
		return;
	}
	if (event.Is<FontEvent>())
	{
		HandleFont();
		return;
	}
}

void Control::Paint(CDC * pDC, const CRect & r)
{
	SaveObjects save(pDC);
	SelectFont(pDC);
	HandlePaint(pDC, r);
}

String  Control::Hint() const
{ 
	return Parent() ? Parent()->Hint() : ""; 
}

const CFont * Control::Font() const
{
	return Parent() ? Parent()->Font() : 0;
}

void Control::SelectFont(CDC * pDC) const
{
	const CFont * pFont = Font();
	if (pFont != 0)
		pDC->SelectObject(const_cast<CFont *>(pFont));
}

CPoint Control::ClientToScreen(CPoint p) const
{
	if (GetWindow() == 0)
		return CPoint(0, 0);
	else {
		p += GetWindowOffset();
		GetWindow()->ClientToScreen(&p);
		return p;
	}
}

CPoint Control::ScreenToClient(CPoint p) const
{
	if (GetWindow() == 0)
		return CPoint(0, 0);
	else {
		GetWindow()->ScreenToClient(&p);
		p -= GetWindowOffset();
		return p;
	}
}

CPoint Control::ClientToParent(CPoint p) const
{
	return p + Offset();
}

CPoint Control::ParentToClient(CPoint p) const
{
	return p - Offset();
}

void Control::InvalidateRect(const CRect & rect)
{
	if (GetWindow() != 0)
	{
		CRect r(rect);
		r.OffsetRect(GetWindowOffset());
		GetWindow()->InvalidateRect(&r, FALSE);
	}
}

void Control::Update()
{
	if (GetWindow() != 0)
	{
		GetWindow()->UpdateWindow();
	}
}

void Control::CaptureMouse()
{
	if (Parent() != 0)
		Parent()->SetCaptureControl(this);
}

void Control::ReleaseMouse()
{
	if (Parent() != 0)
		Parent()->SetCaptureControl(0);
}

bool Control::HasCapture() const
{
	return Parent() != 0 && Parent()->GetCaptureControl() == this;
}

void Control::SetFocus()
{
	if (Parent() != 0) {
		if (CanGetFocus())
			Parent()->SetFocusControl(this);
		else
			Parent()->SetFocus();
	}
}

bool Control::HasFocus() const
{
	return Parent() != 0 && Parent()->GetFocusControl() == this;
}

void Control::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	m_ButtonPressed = true;
	m_DragStart = p;

	SetFocus();
	CaptureMouse();
}

void Control::HandleLeftUp(unsigned int flags, const CPoint & p)
{
	ReleaseMouse();
	m_ButtonPressed = false;
	if (m_Dragging) {
		m_Dragging = false;
		HandleDragging(flags, p);
		HandleDragEnd(flags, p);
	}
}

void Control::HandleMouseMove(unsigned int flags, const CPoint & p)
{
	if (m_Dragging) {
		HandleDragging(flags, p);
	} else 
		if (m_ButtonPressed)
			if (m_Dragging = HandleDragStart(flags, m_DragStart, p))
				HandleDragging(flags, p);
}

void Control::HandleMouseEnter(unsigned int flags, const CPoint & p)
{
//	assert(!m_Inside);

	m_Inside = true;
	UpdateCursor();

#if defined(DEBUG_ENTER_LEAVE)
	const type_info & info = typeid(* this);
	CWnd * pWnd = GetWindow();
	if (pWnd == 0)
		return;
	if (pWnd->GetStyle() & WS_CHILD)
		pWnd = pWnd->GetParentOwner();
	if (pWnd != 0) {
		CString msg;
		msg.Format("Entered: %s", info.name());
		pWnd->SetWindowText(msg);
		Sleep(200);
	}
#endif 

	WindowTipCtrl * pTipCtrl = GetHintWindow();
	if (pTipCtrl != 0) 
	{
		CRect r = GetWindowRect();
		String sHint = Hint();
		pTipCtrl->AddWindow(GetWindow(), sHint.c_str(), &r);
		pTipCtrl->Activate(!sHint.empty());
	}
}

void Control::HandleMouseLeave()
{			  
//	assert(m_Inside);

	m_Inside = false;
	UpdateCursor();

#if defined(DEBUG_ENTER_LEAVE)
	const type_info & info = typeid(* this);
	CWnd * pWnd = GetWindow();
	if (pWnd == 0)
		return;
	if (pWnd->GetStyle() & WS_CHILD)
		pWnd = pWnd->GetParentOwner();
	if (pWnd != 0) {
		CString msg;
		msg.Format("Leaved: %s", info.name());
		pWnd->SetWindowText(msg);
		Sleep(200);
	}
#endif 

	WindowTipCtrl * pTipCtrl = GetHintWindow();
	if (pTipCtrl != 0) 
	{
		pTipCtrl->DelWindow(GetWindow());
	}

}

void Control::HandleFont()
{
//	Invalidate();
}

bool ControlEnabled(const Control * pControl)
{
	if (pControl == 0)
		return true;
	return pControl->Enabled() && ControlEnabled(pControl->Parent());
}

} // My
